

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;


public class Shape {
    private final char id;
    private final List<Puzzle.Cell> cells = new ArrayList<Puzzle.Cell>();

    public Shape(char id) {
        this.id = id;
    }

    public char getId() {
        return id;
    }

    public List<Puzzle.Cell> getCells() {
        return Collections.unmodifiableList(cells);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Shape shape = (Shape) o;

        return id == shape.id;

    }

    @Override
    public int hashCode() {
        return (int) id;
    }

    public void addCell(Puzzle.Cell cell) {
        this.cells.add(cell);
    }

    public void clear() {
        for (Puzzle.Cell cell : cells) {
            cell.removeShapeReference();
        }

        cells.clear();
    }
    
    public boolean equivalent(Shape shape) {
        if (shape.cells.size() != this.cells.size()) { return false; }
        
        return shape.constructBitmap().equals(this.constructBitmap());
    }
    
    private int minX() {
        int min = Integer.MAX_VALUE;

        for (Puzzle.Cell cell : cells) {
            final int col = cell.getCol();
            if (col < min) {
                min = col;
            }
        }
        
        return min;
    }

    private Bitmap constructBitmap() {
        int minRow = Integer.MAX_VALUE;
        int minCol = Integer.MAX_VALUE;
        int maxRow = Integer.MIN_VALUE;
        int maxCol = Integer.MIN_VALUE;

        for (Puzzle.Cell cell : cells) {
            final int row = cell.getRow();
            final int col = cell.getCol();

            if (row < minRow) {
                minRow = row;
            }

            if (row > maxRow) {
                maxRow = row;
            }

            if (col < minCol) {
                minCol = col;
            }

            if (col > maxCol) {
                maxCol = col;
            }
        }

        final Bitmap result = new Bitmap(maxRow - minRow + 1, maxCol - minCol + 1);

        for (Puzzle.Cell cell : cells) {
            final int row = cell.getRow() - minRow;
            final int col = cell.getCol() - minCol;

            result.bits[result.width*row + col] = true;
        }

        return result;
    }
    
    private static class Bitmap {
        final boolean[] bits;
        final int width;
        final int height;

        private Bitmap(int height, int width) {
            this.height = height;
            this.width = width;
            this.bits = new boolean[width*height];
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Bitmap bitmap = (Bitmap) o;

            if (height != bitmap.height) return false;
            if (width != bitmap.width) return false;
            if (!Arrays.equals(bits, bitmap.bits)) return false;

            return true;
        }

        @Override
        public int hashCode() {
            int result = bits != null ? Arrays.hashCode(bits) : 0;
            result = 31 * result + width;
            result = 31 * result + height;
            return result;
        }
    }
}
